package com.gmail.dengtao.joe.game.server;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gmail.dengtao.joe.game.server.client.handler.GameClientHandler;
import com.gmail.dengtao.joe.game.server.config.ServerConfig;
import com.gmail.dengtao.joe.game.server.group.ServerGroupListener;
import com.gmail.dengtao.joe.game.transport.filter.DataFilter;
import com.gmail.dengtao.joe.group.channel.Channel;
import com.gmail.dengtao.joe.group.channel.impl.TCPChannel;
import com.gmail.dengtao.joe.group.exception.ChannelException;
import com.gmail.dengtao.joe.group.member.Member;
import com.gmail.dengtao.joe.transport.Acceptor;
import com.gmail.dengtao.joe.transport.SocketAcceptor;

public class GameServer {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(GameServer.class);
	private final Object acceptorLock = new Object();
	
	private Channel channel;
	private SocketAcceptor acceptor;
	private ServerGroupListener listener;
	private ServerConfig serverConfig;
	
	public GameServer(ServerConfig serverConfig) {
		super();
		this.serverConfig = serverConfig;
	}

	private void startGroup() throws ChannelException {
		
		channel = new TCPChannel(serverConfig.getGroupConfig().getHost(), serverConfig.getGroupConfig().getPort());
		
		listener = new ServerGroupListener();
		
		// Attach the listeners to the channel
		channel.setChannelListener(listener);
		channel.setMembershipListener(listener);
		
		// Config channel receiver
		//	channel.getChannelReceiver().setSelectTimeout(100);
		//	channel.getChannelReceiver().setIdleTimeForRead(5);
		channel.getChannelReceiver().setReceiveBufferSize(serverConfig.getGroupConfig().getReceiveBufSize());
		
		// Config channel sender
		//	channel.getChannelSender().setSelectTimeout(100);
		//	channel.getChannelSender().setIdleTimeForWrite(5);
		channel.getChannelSender().setSendBufferSize(serverConfig.getGroupConfig().getSendBufSize());

		// Open channel
		channel.open();
		
		LOGGER.info("[GAMESERVER][GROUP][STARTED]");
		
		for (Member member : serverConfig.getGroupConfig().getMembers()) {
			channel.addMember(member);
		}
	}
	
	private void startAcceptor() throws Exception {
		acceptor = new SocketAcceptor(serverConfig.getAcceptorConfig().getHost(), serverConfig.getAcceptorConfig().getPort());
		
		// sometimes, you should init before...
		acceptor.init();
		
		// Custom setting...
		acceptor.setSelectTimeout(100);
		acceptor.setSendBufferSize(1024);
		acceptor.setReceiveBufferSize(1024);
		
		// config filter chain
		acceptor.getFilterChain().addLast("DataFilter", new DataFilter());
		
		// Inject the biz handler.
		GameClientHandler handler = new GameClientHandler(channel);
		acceptor.setHandler(handler);
		listener.setHandler(handler);
		
		Thread acceptorThread = new Thread(new AcceptorThread(acceptor, acceptorLock));
		acceptorThread.setDaemon(true);
		acceptorThread.setName("AcceptorThread");
		
		synchronized (acceptorLock) {
			acceptorThread.start();
			acceptorLock.wait();
			LOGGER.info("[GAMESERVER][ACCEPTOR][STARTED][LISTEN /" + serverConfig.getAcceptorConfig().getHost() + ":" + serverConfig.getAcceptorConfig().getPort() + "]");
		}
	}

	private final Object blockLock = new Object();
	private boolean block = false;
	
	public void block() {
		if (block) return;
		synchronized (blockLock) {
			try {
				block = true;
				blockLock.wait();
			} catch (InterruptedException e) {
				LOGGER.error("[GAMESERVER][BLOCK][EXCEPTION]]", e);
			}
			block = false;
		}
	}
	
	public void wakeup() {
		if (!block) return;
		synchronized (blockLock) {
			blockLock.notifyAll();
		}
	}
	
	class AcceptorThread implements Runnable {

		private final Object acceptorLock;
		private Acceptor acceptor;
		
		public AcceptorThread(Acceptor acceptor, Object acceptorLock) {
			this.acceptor = acceptor;
			this.acceptorLock = acceptorLock;
		}

		@Override
		public void run() {
			try {
				LOGGER.info("[GAMESERVER][ACCEPTOR][STARTING...]");
				acceptor.start(acceptorLock);
				LOGGER.info("[GAMESERVER][ACCEPTOR][STOPPED...]");
			} catch (Exception e) {
				LOGGER.error("[GAMESERVER][ACCEPTOR][EXCEPTION]", e);
			}
		}
		
	}
	
	/**
	 * @param args
	 * @throws Exception 
	 * @since 1.0
	 */
	public static void main(String[] args) throws Exception {
	
		ServerConfig serverConfig = new ServerConfig("/config/server.xml");
		
		GameServer server = new GameServer(serverConfig);
		
		// start server group...
		server.startGroup();
	
		// start acceptor...
		server.startAcceptor();
		
		// block thread herr...
		server.block();
	}

}